package myJs.tools

import myJs.Implicits._
import myJs.Utils
import myJs.Utils._
import myJs.myPkg._
import myJs.myPkg.d3.{d3T}
import myJs.myPkg.jquery._
import myJs.tool.Tool
import org.scalajs.dom
import org.scalajs.dom._
import org.scalajs.dom.raw.HTMLFormElement
import scalatags.Text.all._

import scala.collection.immutable.SeqMap
import scala.scalajs.js
import scala.scalajs.js.Dynamic.{global => g}
import scala.scalajs.js.annotation.{JSExport, JSExportTopLevel}

/**
 * Created by yz on 16/9/2021
 */
@JSExportTopLevel("Muscle")
object Muscle {

  val formId = "form"
  var tree: Phylotree = _

  def formJq = $(s"#${formId}")

  @JSExport("init")
  def init = {
    bootStrapValidator
    registerEvent
  }

  def registerEvent = {
    $(document.body).on("click", s".myRun", "", (y: Element) => {
      myRun
    })
    $(document.body).on("click", s".myShowExample", "", (y: Element) => {
      showExample(y)
    })
    $(document.body).on("click", s".myShowExampleFile", "", (y: Element) => {
      showExampleFile
    })
    $(document.body).on("click", s"#sort_original", "", (y: Element) => {
      val index = layer.load(1, Tool.layerOptions.copy(shade=List("0.1", "#fff")))
      tree.resort_children((a: PhyTreeNode, b: PhyTreeNode) => {
        a.original_child_order - b.original_child_order
      })
      layer.close(index)
    })
    $(document.body).on("click", s"#sort_ascending", "", (y: Element) => {
      sortNodes(true)
    })
    $(document.body).on("click", s"#sort_descending", "", (y: Element) => {
      sortNodes(false)
    })
    $(document.body).on("change", s".phylotree-layout-mode", "", (y: Element) => {
      val index = layer.load(1, Tool.layerOptions.copy(shade=List("0.1", "#fff")))
      if ($(y).isChecked) {
        if (tree.radial() != ($(y).myDataStr("mode") == "radial")) {
          tree.radial(!tree.radial()).placenodes().update()
        }
      }
      layer.close(index)
    })
    $(document.body).on("click", s"[data-direction]", "", (y: Element) => {
      spacingChange(y)
    })
    $(document.body).on("change", s".phylotree-align-toggler", "", (y: Element) => {
      val index =layer.load(1, Tool.layerOptions.copy(shade=List("0.1", "#fff")))
      if ($(y).isChecked) {
        if (tree.align_tips($(y).myDataStr("align") == "right").isDdefined) {
          tree.placenodes().update()
        }
      }
      layer.close(index)
    })
    $(document.body).on("click", s".myDownloadFa", "", (y: Element) => {
      downloadFasta
    })
    $(document.body).on("click", s".myDownloadTree", "", (y: Element) => {
      downloadTree
    })
    $(document.body).on("submit", s".mySubmit", "", (y: Element) => {
      mySubmit
    })
  }

  def mySubmit={
    val html= $("svg:first").outerHTML.get.toString
    $("#svgHtml").`val`(html)
    true
  }

  def downloadFasta = {
    val fileName = "fasta.txt"
    val content = $("#out").text()
    Tool.downloadTxt(fileName, content)
  }

  def downloadTree = {
    val fileName = "newick.txt"
    val content =  $("#treeOut").text()
    Tool.downloadTxt(fileName, content)
  }

  def spacingChange(y: Element) = {
    val index =layer.load(1, Tool.layerOptions.copy(shade=List("0.1", "#fff")))
    val amount = $(y).myDataInt("amount")
    if ($(y).myDataStr("direction") == "vertical") {
      tree.spacing_x(tree.spacing_x() + amount).update()
    } else {
      tree.spacing_y(tree.spacing_y() + amount).update()
    }
    layer.close(index)
  }

  def sortNodes(asc: Boolean) = {
    val index =layer.load(1, Tool.layerOptions.copy(shade=List("0.1", "#fff")))
    tree.traverse_and_compute((n: PhyTreeNode) => {
      val maxCountDepth = if (n.children.isDdefined && n.children.size > 0) {
        n.children.map(_.count_depth).max
      } else 0
      n.count_depth = 1 + maxCountDepth
    })
    tree.resort_children((a: PhyTreeNode, b: PhyTreeNode) => {
      val diff = a.count_depth - b.count_depth
      if (asc) diff else -(diff)
    })
    layer.close(index)
  }

  val eg =
    s"""
       |>Example1.mrna1
       |MDPNNCLLLNLKVYNFTNCNASNNSVDLPVNDDWFHPGILYVIPAIYGAIILIGLIGNIT
       |LIKIFCTVKSMRNVPNLFISSLALGDLLLLVTCAPVDASRYLADRWLFGRIGCKLIPFIQ
       |LTSVGVSVFTLTALSADRYKAIVRPMDIQTSHALLKICIKAALIWIISMLLAIPEAVFSD
       |LHPFHEESTNQTFISCAPYPHSNDLHPKIHSMASFLVFYIIPLSIISVYYYFIARNLIQS
       |AYNLPVEGNIHVKKQMESRKRLAKMVLVFVGLFAFCWLPNHVIYLYRSYHYSEVDTSMLH
       |FVTSICARLLAFTNSCVNPFALYLLSKTFRKQFNTQLLCCRPGLMSRSHSTGRSTTCMTS
       |FKSTNPSVATFSLINGNICHEGYV
       |>Example2.mrna1
       |MIKILTTTDGCTDWSVDIKKYQVLVGEPVRIKCALFYGYIRANYSLAQSAGLSLMWYKSS
       |GPGDFEEPIAFDGSRMSKEEDSIWFRPTLLQDSGLYACVIRNSTYCMKVSISLTVGENDT
       |GLCYNSKMKYFEKAELSKSKEISCRDIEDFLLPTREPEILWYKECRTKIWRPSIVFKRDT
       |LLIREVREDDIGNYTCELKYGGFVVRRTTELTEHLGEQEVSISLIVDSVEEGDLGNYSCY
       |VENGNGRRQASVLLHKRELMYTVELAGGLGAILLLLVCLVTIYKCYKIEIMLFYRNHFGA
       |EELDGDNKDYDAYLSYTKVDPDQWNQETGEEERFALEILPDMLEKHYGYKLFIPDRDLIP
       |TGTYIEDVARCVDQSKRLIIVMTPNYVVRRGWSIFELETRLRNMLVTGEIKVILIECSEL
       |RGIMNYQEVEALKHTIKLLTVIKWHGPKCNKLNSKFWKRLQYEMPFKRIEPITHEQALDV
       |SEQGPFGELQTVSAISMAAATSTALATAHPDLRSTFHNTYHSQMRQKHYYRSYEYDVPPT
       |GTLPLTSIGNQHTYCNIPMTLINGQRPQTKSSREQNPDEAHTNSAILPLLPRETSISSVI
       |W
       |""".stripMargin.trim

  def showExampleFile = {
    Tool.downloadTxt("example.fa", eg)
  }

  def showExample(y: Element) = {
    val inputJq = $(y).parents(".form-group").find(":input")
    inputJq.`val`(eg)
    inputJq.parents(s"#${formId}").bv.revalidateField(inputJq)
  }

  def myRun = {
    val bv = formJq.bv
    val resultJq = $("#result")
    bv.validate()
    val valid = bv.isValid()
    if (valid && inputCheck(formId)) {
      val formData = new FormData(document.getElementById(formId).asInstanceOf[HTMLFormElement])
      val element = div(id := "content",
        span(id := "info", "Query...",
          span(id := "progress")), " ",
        img(src := "/assets/images/running2.gif", cls := "runningImage", width := 30, height := 20)
      ).render
      val layerOptions = LayerOptions(icon=16,shade=0.01,time=20000000)
      val index = layer.msg(element, layerOptions)
      val url = g.jsRoutes.controllers.ToolsController.muscle().url.toString
      val xhr = new XMLHttpRequest
      xhr.open("post", url)
      xhr.upload.onprogress = Utils.progressHandlingFunction
      xhr.onreadystatechange = (e) => {
        if (xhr.readyState == XMLHttpRequest.DONE) {
          layer.close(index)
          val rs = xhr.responseText.toJDJAn
          val valid = rs("valid").toB
          if (valid) {
            $("#out").html(rs.myGet("out"))
            if (rs.myGet("tree").isBlank) {
              $("#treeContent").hide()
            } else {
              val nwk = rs.myGet("tree")
              $("label").removeClass("active")
              $("#defaultAlign,#linear").addClass("active")
              $("#treeOut").html(nwk)
              val phylotreeOptions = PhylotreeOptions
              val d3=g.d3.asInstanceOf[d3T]
              val element = d3.select(s"#tree_display")
              tree = Phylotree().svg(element).radial(false).spacing_x(75, true).
                spacing_y(100, true)
              tree = tree(nwk).layout()
              $("#treeContent").show()
            }
            $("#result").show()
          } else {
            g.swal("Error", rs.myGet("message"), "error")
            resultJq.hide()
          }
        }
      }
      xhr.send(formData)
    }
  }

  def inputCheck(formId: String) = {
    val text = $(s"#${formId} #queryText").`val`.toString
    val file = $(s"#${formId} #input-1").`val`.toString
    if (file.nonEmpty && text.nonEmpty) {
      Swal.swal(SwalOptions.title("Error").text("Please either enter sequences into the box or upload a file, not both!").
        `type`("error"))
      false
    } else if (file.isEmpty && text.isEmpty) {
      Swal.swal(SwalOptions.title("Error").text("Please enter sequence or upload file!").
        `type`("error"))
      false
    } else true
  }

  def bootStrapValidator = {
    val maxNumber = Double.MaxValue
    val dict: js.Dictionary[js.Any] = js.Dictionary(
      "feedbackIcons" -> js.Dictionary(
        "valid" -> "glyphicon glyphicon-ok",
        "invalid" -> "glyphicon glyphicon-remove",
        "validating" -> "glyphicon glyphicon-refresh",
      ),
      "fields" -> js.Dictionary(
        "queryText" -> {
          val info = "Sequences"
          js.Dictionary(
            "validators" -> js.Dictionary(
              "regexp" -> js.Dictionary(
                "message" -> s"${info} is invalid(at least two sequences)!",
                "regexp" -> js.RegExp("^(>.*\\n[a-zA-Z\\n]+\\s*){2,}$"),
              )
            )
          )
        },
        "file" -> {
          val info = "Sequences file"
          js.Dictionary(
            "validators" -> js.Dictionary(
              "file" -> js.Dictionary(
                "message" -> s"${info} must is a fa file！",
                "extension" -> "fa",
              ),
            )
          )
        },
      )
    )
    formJq.bootstrapValidator(dict)
  }

}
